package com.melon.redis.serial;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;

import javax.annotation.Resource;
import java.text.NumberFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 序列号生成器
 *
 * @author melon
 * @version 1.0
 * @since JDK1.8
 */
public class SerialNumberGenerator {

    private static final String COLON = ":";

    @Resource
    private RedisTemplate<String, Long> redisTemplate;

    /**
     * 生成编号
     *
     * @param config 配置
     * @return {@link String}
     */
    public synchronized String generate(SerialNumberConfig config) {
        return generate(config, 1).get(0);
    }


    /**
     * 批量生成编号
     *
     * @param config 配置
     * @return {@link String}
     */
    public synchronized List<String> generate(SerialNumberConfig config, int delta) {
        String day = getCurrentDay();
        String key = getValueKey(config, day);
        RedisAtomicLong counter = getRedisAtomicLong(key);
        if (delta == 0) {
            delta = config.getDelta();
        }
        List<String> serialNos = new ArrayList<>(delta);
        long serialNo = getSerialNo(counter, delta);
        String prefix = config.getPrefix();
        for (int i = 1; i <= delta; i++) {
            serialNos.add(prefix + format(serialNo + i, day, config.getLength()));
        }
        return serialNos;
    }


    /**
     * 获取获取当前日期
     */
    private String getCurrentDay() {
        return LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
    }

    /**
     * 生成redis的key
     */
    private String getValueKey(SerialNumberConfig config, String day) {
        return config.getRedisKey() + COLON + day;
    }


    /**
     * 生成序列号
     *
     * @param counter 计数器
     */
    private long getSerialNo(RedisAtomicLong counter, long delta) {
        long serialNo = counter.getAndAdd(delta);
        //设置 key 过期时间
        if (serialNo == 0) {
            counter.expire(getRemainSecondsOneDay(), TimeUnit.SECONDS);
        }
        return serialNo;
    }

    /**
     * 得到RedisAtomicLong
     *
     * @param key 关键
     * @return {@link RedisAtomicLong}
     */
    private RedisAtomicLong getRedisAtomicLong(String key) {
        if (redisTemplate == null || redisTemplate.getConnectionFactory() == null) {
            throw new IllegalArgumentException("获取连接失败");
        }
        return new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
    }


    /**
     * 获取过期时间
     */
    private long getRemainSecondsOneDay() {
        LocalDateTime current = LocalDateTime.now();
        return ChronoUnit.SECONDS.between(current, LocalDateTime.of(current.toLocalDate(), LocalTime.MAX));
    }


    /**
     * 格式化
     */
    private String format(Long num, String day, Integer length) {
        String id = String.valueOf(num);
        if (id.length() < length) {
            NumberFormat nf = NumberFormat.getInstance();
            nf.setGroupingUsed(false);
            nf.setMaximumIntegerDigits(length);
            nf.setMinimumIntegerDigits(length);
            id = nf.format(num);
        }
        return day + id;
    }
}